/**
 * SPDX-FileCopyrightText: 2023-2025 Sangfor Technologies Inc.
 * SPDX-License-Identifier: Mulan PSL v2
 */
/**
 * Created by hjh on 2018/7/6.
 * 名字：input.js
 * 功能：input类型通用JS
 * 包括：
 *        --------------------------------------------------------------------------------------------
 *        1--IP相关检测及转换函数
 *
 *        1>-isIP                检测IP是否合法                        2>-isMask                    检测掩码是否合法
 *        3>-IPatol            IP字符串转为long                    4>-isIPMaskMatch    IP与掩码是否匹配
 *
 *
 *
 *        5>-isNet            是否是子网地址                        6>-isNetMaskMatch 子网与掩码是否匹配
 *
 *
 *
 *        7>-isIPConflict检测两IP/掩码网段冲突        8>-isNetConflict    检测两子网网段是否冲突
 *        8>-isIPRangeVal检测IP范围是有效                    10>isIPRangeConflict检测两IP范围是否互相包含
 *
 *        2--端口相关检测及转换函数
 *        1>-isPort                    检测端口是否合法            2>-isPortRangeConflict    检测两端口范围是否冲突
 *        3>-isPortRangeVal    检测端口范围是否有效
 *
 *
 *        3--字符串相关
 *
 *        1>-strlen                字符串长度(返回字符串所占字节数)
 *        ---------------------------------------------------------------------------------------------------
 */

/*******************************************************************
 **IP相关
 ********************************************************************/

/* eslint-disable */

/**
 * 将字符串形式的IP转换成long
 * @param {String} strIP
 * @returns {Number}
 * */
function IPatol (strIP: string) {
    let ip = 0;
    let array = strIP.split('.');

    if (array.length === 4) {
        ip = Number(ip) + Number(array[0]) * 16777216;
        ip = Number(ip) + Number(array[1]) * 65536;
        ip = Number(ip) + Number(array[2]) * 256;
        ip = Number(ip) + Number(array[3]);
    }

    return parseInt(ip+'');
}

/**
 * IP是否合法,无提示,0.0.0.0 被认为是不合法IP，如果IP可为0.0.0.0需在外部另加判断
 * @param {String} dwIP
 * @returns {String}
 * */
function IPntoa (dwIP: string): string {
    let part1 = ((dwIP as SafeAny) >> 24) & 0x000000FF;
    let part2 = ((dwIP as SafeAny) >> 16) & 0x000000FF;
    let part3 = ((dwIP as SafeAny)>> 8) & 0x000000FF;
    let part4 = ((dwIP as SafeAny) & 0x000000FF);

    return `${part1.toString()}.${part2.toString()}.${part3.toString()}.${part4.toString()}`;
}

/**
 * IP是否合法,无提示,0.0.0.0 被认为是不合法IP，如果IP可为0.0.0.0需在外部另加判断
 * 错误提示：IP无效!（只能包含0-255之间的整数,且首位必须是1-223之间除127以外的数字,末位不能为0或255）'
 * @param {String} strIP
 * @returns {Boolean}
 * */
function isIP (strIP:string): boolean {
    let re = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/; //IP正则
    if (re.test(strIP)) {
        let n1 = Number(RegExp.$1);
        let n2 = Number(RegExp.$2);
        let n3 = Number(RegExp.$3);
        let n4 = Number(RegExp.$4);

        let array = strIP.split('.');

        if ((array[0].charAt(0) === '0' && n1 !== 0)
            || (array[1].charAt(0) === '0' && n2 !== 0)
            || (array[2].charAt(0) === '0' && n3 !== 0)
            || (array[3].charAt(0) === '0' && n4 !== 0))
            return false;


        //首位必需为1-223之间除127之外的任一数字,末位不能为0或255
        if ((n1 > 0 && n1 < 224 && n1 !== 127) &&
            (n2 >= 0 && n2 <= 255) &&
            (n3 >= 0 && n3 <= 255) &&
            (n4 > 0 && n4 < 255)
        ) {
            return true;
        }
    }
    return false;
}

//

/**
 * IP是否合法，宽松的IP校验规则
 * @param {String} strIP
 * @returns {Boolean}
 * */
function isIP2 (strIP: string): boolean {
    let re = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/; //IP正则

    if (re.test(strIP)) {
        let n1 = Number(RegExp.$1);
        let n2 = Number(RegExp.$2);
        let n3 = Number(RegExp.$3);
        let n4 = Number(RegExp.$4);

        let array = strIP.split(".");

        if ((array[0].charAt(0) === '0' && n1 !== 0)
            || (array[1].charAt(0) === '0' && n2 !== 0)
            || (array[2].charAt(0) === '0' && n3 !== 0)
            || (array[3].charAt(0) === '0' && n4 !== 0)
        ) {
            return false;
        }

        if ((n1 >= 0 && n1 <= 255) &&
            (n2 >= 0 && n2 <= 255) &&
            (n3 >= 0 && n3 <= 255) &&
            (n4 >= 0 && n4 <= 255)
        ) {
            return true;
        }
    }

    return false;
}

/**
 * 掩码是否合法, 0.0.0.0 和 255.255.255.255 都不合法
 * @param {String} strMask
 * @returns {Boolean}
 * */
function isMask (strMask: string): boolean {
    let reg = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/;//IP正则

    if (reg.test(strMask)) {
        let n1 = Number(RegExp.$1);
        let n2 = Number(RegExp.$2);
        let n3 = Number(RegExp.$3);
        let n4 = Number(RegExp.$4);
        if (isNaN(n1) || isNaN(n2) || isNaN(n3) || isNaN(n4)) {
            return false;
        }
        if ((n1 > 255 || n1 < 0) ||
            (n2 > 255 || n2 < 0) ||
            (n3 > 255 || n3 < 0) ||
            (n4 > 255 || n4 < 0)
        ) {
            return false;
        }

        let tmp = (n1 * Math.pow(2, 24)) + (n2 * Math.pow(2, 16)) + (n3 * Math.pow(2, 8)) + n4;

        if (tmp < Math.pow(2, 31)) {
            return false;
        } else {
            tmp -= Math.pow(2, 31);
            //后续位必须为连续的1
            for (let i = 30; i > 1; i--) {
                if (tmp === 0) {
                    break;
                }
                else if (tmp < Math.pow(2, i)) {
                    return false;
                }
                else {
                    tmp -= Math.pow(2, i);
                }
            }
        }
    } else {
        return false;
    }

    return true;
}

/**
 * 掩码是否合法,0.0.0.0是合法的
 * @param {String} strMask
 * @returns {Boolean}
 * */
function isMask2 (strMask: string): boolean {
    if (!isMask(strMask)) {
        return strMask === '0.0.0.0';
    }

    return true;
}

/**
 * P与掩码匹配验证,在执行此函数前，需已对IP及掩码的有效性做过检验
 * @param {String} strIP
 * @param {String} strMask
 * @returns {Boolean}
 * */
function isIPMaskMatch (strIP: string, strMask:string): boolean {
    let nIP = IPatol(strIP);
    let nMask = IPatol(strMask);
    let notMatch = ((nIP | nMask) === -1) || (((nIP | nMask) + Math.pow(2, 32)) === nMask);

    return !notMatch;
}

/**
 * 验证是否是合法的子网地址,0.0.0.0认为是不合法，首位为1-223之间除127以外的数字
 * @param {String} strNet
 * @returns {Boolean}
 * */
function isNet (strNet: string): boolean {
    let re = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/; //IP正则

    if (re.test(strNet)) {
        let n1 = Number(RegExp.$1);
        let n2 = Number(RegExp.$2);
        let n3 = Number(RegExp.$3);
        let n4 = Number(RegExp.$4);

        //首位为1-223之间除127以外的数字
        if ((n1 > 0 && n1 < 224 && n1 !== 127) &&
            (n2 >= 0 && n2 <= 255) &&
            (n3 >= 0 && n3 <= 255) &&
            (n4 >= 0 && n4 <= 255)
        ) {
            return true;
        }
    }

    return false;
}

//验证是否是合法的子网地址,0.0.0.0认为是合法
/**
 * 验证是否是合法的子网地址,0.0.0.0认为是合法
 * @param {String} strNet
 * @returns {Boolean}
 * */
function isNet2 (strNet: string): boolean {
    let re = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/; //IP正则

    if (re.test(strNet)) {
        let n1 = Number(RegExp.$1);
        let n2 = Number(RegExp.$2);
        let n3 = Number(RegExp.$3);
        let n4 = Number(RegExp.$4);

        //首位为1-223之间除127以外的数字
        if ((n1 >= 0 && n1 < 224 && n1 !== 127) &&
            (n2 >= 0 && n2 <= 255) &&
            (n3 >= 0 && n3 <= 255) &&
            (n4 >= 0 && n4 <= 255)
        ) {
            return true;
        }
    }

    return false;
}

/**
 * 验证子网与掩码的匹配,在执行此函数之前，需对子网及掩码的有效性做验证
 * @param {String} strNet
 * @param {String} strMask
 * @returns {Boolean}
 * */
function isNetMaskMatch (strNet: string, strMask: string): boolean {
    let tmpArray: Array<string | number> = strNet.split('.');
    let tmpArray2: Array<string | number> = strMask.split('.');
    if (tmpArray.length !== 4 || tmpArray2.length !== 4) {
        return false;
    }
    for (let i = 0; i < 4; i++) {
        tmpArray[i] = Number(tmpArray[i]);
        tmpArray2[i] = Number(tmpArray2[i]);
        if ((+tmpArray[i] | +tmpArray2[i]) !== tmpArray2[i]) {
            return false;
        }
    }

    return true;
}

/**
 * 判断两IP是否在同一网段下
 * @param {String} ip1
 * @param {String} ip2
 * @param {String} mask
 * @returns {Boolean}
 */
function isInSameIPAddress (ip1: string, ip2: string, mask: string) {
    let ip1Array: Array<string | number> = ip1.split('.'),
        ip2Array: Array<string | number> = ip2.split('.'),
        maskArray = mask.split('.');

    let ip1List: number[] = [], ip2List: number[] = [];

    for(let i = 0; i < 4; i++){
        ip1List.push(+ip1Array[i] & +maskArray[i]);
        ip2List.push(+ip2Array[i] & +maskArray[i]);
    }

    return ip1List.join('.') === ip2List.join('.');
}

/**
 * 验证两IP/掩码 网段是否冲突,在调用该函数前，需已检测传入参数的有效性
 * @param {String} strIP1
 * @param {String} strMask1
 * @param {String} strIP2
 * @param {String} strMask2
 * @returns {Boolean}
 * */
function isIPConflict (strIP1: string, strMask1: string, strIP2: string, strMask2: string): boolean {
    let ip1 = IPatol(strIP1);
    let mask1 = IPatol(strMask1);
    let ip2 = IPatol(strIP2);
    let mask2 = IPatol(strMask2);

    if (ip1 === 0 || mask1 === 0 || ip2 === 0 || mask2 === 0) {
        return false;
    }

    return (ip1 & mask1 & mask2) === (ip2 & mask2 & mask1);
}


/**
 * 验证两子网网段是否冲突(具有包含关系),在调用该函数前，需已检测传入参数的有效性
 * @param {String} strNet1
 * @param {String} strMask1
 * @param {String} strNet2
 * @param {String} strMask2
 * @returns {Boolean}
 * */
function isNetConflict (strNet1: string, strMask1: string, strNet2: string, strMask2: string): boolean {
    let net1 = IPatol(strNet1);
    let mask1 = IPatol(strMask1);
    let net2 = IPatol(strNet2);
    let mask2 = IPatol(strMask2);

    if (net1 === 0 || mask1 === 0 || net2 === 0 || mask2 === 0) {
        return false;
    }

    return (net1 & mask2) === (net2 & mask1);
}

/**
 * 验证IP范围是否有效(起始IP不能大于结束IP)
 * @param {String} strIPFrom
 * @param {String} strIPTo
 * @returns {Boolean}
 * */
function isIPRangeVal (strIPFrom: string, strIPTo: string): boolean {
    let ip1 = IPatol(strIPFrom);
    let ip2 = IPatol(strIPTo);

    return ip1 <= ip2;
}

/**
 * 验证IP范围冲突(即是否具有包含关系),传入参数的有效性在调用该函数前已验证
 * @param {String} IPFrom1
 * @param {String} IPTo1
 * @param {String} IPFrom2
 * @param {String} IPTo2
 * @returns {Boolean}
 * */
function isIPRangeConflict (IPFrom1: string, IPTo1: string, IPFrom2: string, IPTo2: string) {
    let ipf1 = IPatol(IPFrom1);
    let ipt1 = IPatol(IPTo1);
    let ipf2 = IPatol(IPFrom2);
    let ipt2 = IPatol(IPTo2);

    return !((ipf1 > ipt2) || (ipt1 < ipf2)); //IP范围具有包含关系
}

/********************************************************************
 **端口相关
 *********************************************************************/
/**
 * 是否是有效的端口,1-65535之间的数
 * @param {String|Number} strPort
 * @returns {Boolean}
 * */
function isPort (strPort: string | number): boolean {
    let nport = Number(strPort);
    let notPort = isNaN(nport) || nport < 1 || nport > 65535;

    return !notPort;
}

/**
 * 是否是有效的端口，验证规则比较宽松，0-65535之间的数,
 * @param {String|Number} strPort
 * @returns {Boolean}
 * */
function isPort2 (strPort: string | number): boolean {
    let nport = Number(strPort);
    let notPort = isNaN(nport) || nport < 0 || nport > 65535;

    return !notPort;
}

/**
 * 是否是有效的端口范围
 * @param {String|Number} strPort
 * @param {String|Number} strPort2
 * @returns {Boolean}
 * */
function isPortRangeVal (strPort1: string | number, strPort2: string | number) {
    let nport1 = Number(strPort1);
    let nport2 = Number(strPort2);

    if (isNaN(nport1) || isNaN(nport2)) {
        return false;
    }

    return nport1 <= nport2;
}

type Port = string | number

/**
 * 端口范围是否冲突
 * @param {String|Number} PortF1
 * @param {String|Number} PortT1
 * @param {String|Number} PortF2
 * @param {String|Number} PortT2
 * @returns {Boolean}
 * */
function isPortRangeConflict (PortF1: Port, PortT1: Port, PortF2: Port, PortT2: Port) {
    let nf1 = Number(PortF1);
    let nt1 = Number(PortT1);
    let nf2 = Number(PortF2);
    let nt2 = Number(PortT2);

    if (isNaN(nf1) || isNaN(nt1) || isNaN(nf2) || isNaN(nt2)) {
        return false;
    }

    return (nf1 > nt2) || (nt1 < nf2);
}


/********************************************************************
 **字符串相关
 *********************************************************************/

/**
 * 取得字符串的字节数(一个汉字占三个字节)
 * @param {String|Number} str
 * @returns {Boolean}
 * */
function strlen (str: string) {
    let cnt = 0;

    for (let i = 0; i < str.length; i++) {
        let value = str.charCodeAt(i);
        if (value < 0x080) {
            cnt += 1;
        } else if (value < 0x0800) {
            cnt += 2;
        } else {
            cnt += 3;
        }
    }

    return cnt;
}

/**
 * 验证IP范围是否有效(起始IP不能大于结束IP)
 * @param {String} strIP
 * @param {String} strIPFrom
 * @param {String} strIPTo
 * @returns {Boolean}
 * */
function isIPinRange (strIP: string, strIPFrom: string, strIPTo:string): boolean {
    let ip = IPatol(strIP);
    let ip1 = IPatol(strIPFrom);
    let ip2 = IPatol(strIPTo);

    if (ip1 > ip2) {
        return false;	//起始IP大于结束IP
    }

    if (ip < ip1 || ip > ip2) {
        return false;
    }

    return true;
}

/********************************************************************
 **webagent相关
 *********************************************************************/
/**
 * 判断strIpPort是否是ip:port的形式。比如1.2.3.4#1.2.3.5:4009
 * @param {String} strIpPort
 * @param {Boolean} needPort 是否必须带上端口
 * @returns {Boolean}
 * */
function isIPPort (strIpPort: string, needPort = false): boolean {
    const MIN = 0;
    const MAX = 65536;

    const IP_PORT_REG = /^(\d{1,3}\.){3}\d{1,3}(:\d+)?$/;

    let host, port;
    let matchs = strIpPort.split(';');

    if (matchs.length > 4) {
        return false;
    }

    for (let i = 0, len = matchs.length; i < len; i++) {
        let matchItem = matchs[i];

        if (!IP_PORT_REG.test(matchItem)) {
            return false;
        }

        if (matchItem.indexOf(':') > -1) {
            let match = matchItem.split(':');

            host = match[0];
            port = +match[1];

        } else {
            if (needPort) {
                return false;
            }

            host = matchItem;
            port = 1;
        }

        if (!isIP(host) || !port || (port < MIN || port > MAX)) {
            return false;
        }
    }

    return true;
}


/**
 * 区别于strIpPort；允许多个ip并且只有最后一个ip可带上端口的形式。比如1.2.3.4;1.2.3.5:4009（正确）
 * 而1.2.3.4:4009;1.2.3.5（错误）
 * @param {String} strIpPort
 * @param {Boolean} needPort 是否必须带上端口
 * @returns {Boolean}
 * */
function isStrictIPPort (strIpPort: string, needPort = false): boolean {
    const MIN = 0;
    const MAX = 65535;

    const IP_PORT_REG = /^(\d{1,3}\.){3}\d{1,3}(:\d+)?$/;

    let host, port;
    let matchs = strIpPort.split(';');

    if (matchs.length > 4) {
        return false;
    }

    let lastMatchItem = matchs.pop();

    if (!IP_PORT_REG.test(lastMatchItem!)) {
        return false;
    }

    if (lastMatchItem!.indexOf(':') > -1) {
        let match = lastMatchItem!.split(':');

        host = match[0];
        port = +match[1];

    } else {
        if (needPort) {
            return false;
        }

        host = lastMatchItem;
        port = 1;
    }

    if (!isIP(host!) || !port || (port < MIN || port > MAX)) {
        return false;
    }


    for (let i = 0, len = matchs.length; i < len; i++) {
        let matchItem = matchs[i];

        if (!isIP(matchItem)) {
            return false;
        }
    }

    return true;
}

/**
 * 判断strDomainPort是否是 动态域名:端口 的形式
 * @param {String} strDomainPort
 * @returns {Boolean}
 * */
function isDomainPort (strDomainPort: string): boolean {
    if (strDomainPort === null || strDomainPort.length === 0) {
        return false;
    }

    let rex = /^([\w-]+\.)+[a-zA-Z]+:(\d){1,5}$/;
    let arr = strDomainPort.split(':');

    if (!rex.test(strDomainPort)) {
        return false;
    }

    return isPort(arr[1]);
}

/**
 * 判断strUrl是否是url形式
 * @param {String} strUrl
 * @returns {Boolean}
 * */
function isUrl (strUrl: string): boolean {
    let rex = /^([\w-]+\.)+[\w]+(:(\d){1,5})?(\/[\w- .?%&=]*)+(\/)?$/;

    if (strUrl === null || strUrl.length === 0) {
        return false;
    }

    strUrl = encodeURI(strUrl); //以防路径名有中文

    return rex.test(strUrl);
}

/**
 * 从strWebagt获取正确的webagent地址
 * 首先自动转换句号冒号到英文, 然后判断webagent是否为下面的格式:
 * 1> 固定IP:端口(其中固定IP支持多IP,最多支持四个IP,各IP之间以#号分隔,如:202.96.137.75#60.28.239.21:4009);
 * 2> 动态域名:端口(如:www.geniusai.com.cn:4009);
 * 3> 动态网页(如:www.geniusai.com/NG4.0/test.php).
 * 如果不是返回null,如果是则返回转换得到的地址
 * @param {String} strWebagt
 * @returns {String|Null}
 * */
function getRightWebagent (strWebagt: string): string | null {
    if (strWebagt === null || strWebagt.length === 0) {
        return null;
    }

    strWebagt = strWebagt.replace(/^http(:|：)\/\//, ''); //如果http://头 则去掉

    // 将'/'以前的句号冒号全部替换成英文的
    let arr = strWebagt.split('/');
    arr[0] = arr[0].replace(/：/g, ':');
    arr[0] = arr[0].replace(/。/g, '.');

    strWebagt = arr.join('/');

    if (isIPPort(strWebagt) || isDomainPort(strWebagt) || isUrl(strWebagt)) {
        return strWebagt;
    }

    return null;
}

/**
 * 密码统一的校验方法
 * @param {String} v
 * @return {Boolean|String}
 * */
function pwdValidator (v = ''): boolean | string {
    let reg = /^[a-zA-Z0-9@\{\}\[\]\(\)\+\-\.\?\|\_]+$/;

    if (!v || reg.test(v)) {
        return true;
    }

    return _('该输入项只能由数字、字母、()[]{}@|.?_-+ 组成');
}

export {
    IPatol,
    IPntoa,
    isIP,
    isIP2,
    isMask,
    isMask2,
    isIPMaskMatch,
    isNet,
    isNet2,
    isNetMaskMatch,
    isInSameIPAddress,
    isIPConflict,
    isNetConflict,
    isIPRangeVal,
    isIPRangeConflict,
    isPort,
    isPort2,
    isPortRangeVal,
    isPortRangeConflict,
    strlen,
    isIPinRange,
    isIPPort,
    isStrictIPPort,
    isDomainPort,
    isUrl,
    getRightWebagent,
    pwdValidator
}
